#include <string>
#include <stdio.h>
#include <math.h>
#include <fstream>
#include <sstream>
#include <iostream>
#include "View.h"
#include "../Connector/NetworkConnector.h"
#include "../Connector/ServerConnector.h"
#include "../Connector/Connector.h"
#include "../Logger/Logger.h"
const int View::MAX_PLAYER_SINGLE_MACHINE = 3;
void View::showCurrentPos() const
{
  int x = 0;
  int y = 0;
  int r = COLS / 2;
  int n = model_->getPlayersNumber();
  int centerX = COLS / 2;
  int centerY = LINES / 2;

  int coeff = COLS / LINES;

  std::string name = "";
  clear();
 /* if (duelWinner_ != 0)
  {
    std::stringstream s;
    s << model_->getPlayerName(model_->getDuelWinner()) << " wins the duel!";
    printCenter(s.str());
    sleep(1);
    refresh();
  }
  clear();*/
  for (int i = 0; i != n ; ++i)
  {
    //x = centerX + cos(2 * M_PI / n * i) * r;
    //y = centerY + sin(2 * M_PI / n * i) * r / coeff;
    y = (i  / 3) * 30 + 1;
    x = (COLS *(i % 3) / n);//COLS / 5 + COLS * 4 / 5 * i / n;
    name = model_->getPlayerName(i + 1);
    move(y, x); 

    //prints player's name
    printw("%s", name.c_str());

    move(++y, x);
    printw("Opened: %i",model_->getOpenedCardsNumber(i + 1));

    move(++y, x);
    printw("Closed: %i",model_->getClosedCardsNumber(i + 1));
    printMovePlace(++y, COLS * (model_->getCurrentTurn() - 1) / n);
    printCard(++y, COLS * i / n, model_->getTopCardID(i + 1), model_->getTopCardColor(i + 1));
  }
 

  std::string message = model_->getPlayerName(model_->getCurrentTurn());
  message += "'s turn";
  printTop(message);
  printDuelMessage(LINES - 5);
  curs_set(0);
   refresh();
}

void View::printPlayersList() const
{
  clear();
  if ((gameType == NETWORK) && (userType == SERVER))
  {
    move(LINES / 2, COLS / 2);
    printw("Press %s to start the game", settings_.get("START_KEY").c_str());
  }

  int y = 0;
  int x = COLS / 2;
  int n = model_->getPlayersNumber();
  move(y++, x);
  printw("CurrentPlayers:\n");
  
  for (int i = 1 ; i <= n; ++i)
  {
    clrtoeol();
    move(y++, x);
    printw("%i.%s\n",i,model_->getPlayerName(i).c_str());
  }
  move(y, x);
  refresh();
}

void View::printMovePlace(int y, int x) const
{
  std::ifstream f;
  f.open(settings_.get("CARD_PLACE_FILENAME").c_str());

  if (f == 0)
  {
    f.close();
    return;
  }

  char string[256];
  while(!f.eof())
  {
    f.getline(string, 256);
    move(y++, x);
    addstr(string);
  }
}

void View::printDuelMessage(int y) const
{
  int x = 0;
  //int y = LINES - 1;
  int num = model_->getPlayersNumber();
  int n = playersNumber_;
  
  for (int i = 0; i != n ; ++i)
  {
    x = (COLS *((players_[i]->getSeat() - 1) % 3) / num);
    //move(y, x);
    std::stringstream s;
    s << "MOVE " << y << " " << x;
    log(s.str());
    if (players_[i]->getSeat() == model_->getDuelWinner()) 
    {
      mvaddstr(y, x, "You win the duel!");
    }
    else if (players_[i]->getSeat() == model_->getDuelLooser())
    {
      mvaddstr(y, x, "You loose the duel :(");
    }
    else if (players_[i]->getSeat() == model_->getWrongDuelist())
    {
      mvaddstr(y, x, "Be more attentive!");
    }
  }
}

void View::getPlayersNames(Connector *connector)
{
  char name[256];
  for (int i = 0; i != playersNumber_; ++i)
  {
    clear();
    move(LINES / 2, COLS / 2);
    curs_set(1);
    printw("Player %i, enter name, please", i + 1);
    move(LINES / 2 + 1, COLS / 2);
    getstr(name);
    log("NAME RECIEVED");
    int error = 0;
    if (connector->connect(name, time(0), error) <= 0)
    {
      log("LATE REGISTRATION");
      printCenter("Game started without you");
      connector->disconnect();
      sleep(1);
    }
   
  }
  clear();
  refresh();
}

void View::connectPlayers()
{
  char name[256];
  int error = 0;
  if (gameType == LOCAL)
  {
     //input player's names
    for (int i = 0; i < playersNumber_; ++i)
    {
       clear();
       move(LINES / 2, COLS / 2);
       curs_set(1);
       printw("Player %i, enter name, please", i + 1);
       move(LINES / 2 + 1, COLS / 2);
       getstr(name);
       //connect to model
       time_t connectTime = time(NULL);
       players_[i]->connect(name, connectTime, error);
       if (error != 0)
       {
          printCenter("Couldn't connect");
          curs_set(0);
          sleep(1);
          //client->disconnect();
          //endwin();
          return;
       }
       else
       {
         printCenter("Connected succesfully!");
         curs_set(0);
         sleep(1);
       }
    }
    //start game
    curs_set(0);
    clear();
    connector_->startGame(true);
  }
  else
  {
    if (userType == CLIENT)
    {
      //NetworkConnector* client = (NetworkConnector*) connector_;
      log("CLIENT");
      char IPAddr[256];
      bool flag = false;
      while (!flag)
      {
        printCenter("Enter server's IP or quit");
        move(LINES / 2 + 1, COLS / 2);
        getstr(IPAddr);
        if (IPAddr == settings_.get("KEY_QUIT"))
        {
          endwin();
          return;
        }
        log("try to connect");
        int error  = client->connectToServer(IPAddr);
        if (error == 1)
        {
          log("error1");
          printCenter("Error opening socket");
        }
        else if (error == 2)
        {
          log("error2");
          printCenter("Error connecting to server");
        }
        else
        {
          //log("no error");printCenter("Connected succesfully!");
          printCenter("Connection is established");
          flag = true;
        }
        sleep(1); 
      }

      getPlayersNames(client);
 
      while(1)
      {

        int exit = client->getPlayersFromServer();
     
        int seat = client->getConfirmedSeat();
     
        if (seat != 0)
        { 
          //MANY PLAYERS!!!!
          //NAME
          players_[0]->setSeat(seat);
          printCenter("Connected succesfully!");
          sleep(1);
        }
     
        if (exit == 1)
        {
          return;
        }
        printPlayersList();
      }
    }


    else if (userType == SERVER)
    { 
      //log("SERVER");
      //ServerConnector* server = (ServerConnector*) connector_;
      server->open();
      getPlayersNames(server);
      players_[0]->setSeat(1);
      while(1)
      {


        printPlayersList();
        int exit = server->acceptClients();
        if (exit == 1)
        {
          return;
        }
      }
    }
  }
}


void View::networkGameCycle()
{
  if (!model_->isGameStarted())
  {
    return;
  }
  printCenter("Game starts right now!");
  sleep(1);
  char ch = 0;
  std::string quitString = settings_.get("KEY_QUIT");  
  bool toExit = false;

  bool flag = true;
  while(1)
  {
    if (flag)
    {
      duelWinner_ = model_->getDuelWinner();
    }
    else
    {
      duelWinner_ = 0;
    }
   
    flag = true;
    
    showCurrentPos();
    ch = 0;
    if (userType == CLIENT)
    {
      ch = client->getMove(toExit);
    }
    else
    {
      ch = server->getMove(toExit);
    }
    if (toExit)
    {
      return;
    }
    if (ch != 0)
    {
      std::stringstream quitStr;
      quitStr << ch;

      if (quitString == quitStr.str())
      {
        if (userType == CLIENT)
        {
          client->quit(players_[0]->getSeat());
        }
        else
        {
          server->quit(players_[0]->getSeat());
        }
        return;
      }

      log("ch != 0");
      //ask players for key
      int keyFound = true;
      for (int i = 0; i < playersNumber_; ++i)
      {
        keyFound = players_[i]->checkKey(ch, i + 1);
        if (keyFound == 1)
        {
          flag = false;
          log("KEY FOUND!");
          if(userType == SERVER)
          {
            server->openCard(players_[i]->getSeat());
          }
          else
          {
            client->openCard(players_[i]->getSeat());
          }
          break;
        }
        else if (keyFound == 2)
        {
          flag = false;
          log("KEY FOUND!");
          if(userType == SERVER)
          {
            server->getTotem(players_[i]->getSeat());
          }
          else
          {
            client->getTotem(players_[i]->getSeat());
          }
          break;
        }
      } 
    }
    
    if (!model_->isGameStarted())
    {
      int winner = model_->getWhoIsWinner();
      std::string finish;

      if (winner != 0)
      {
        finish = model_->getPlayerName(winner);
        finish += " is winner! Bye!";
      }
      else
      {
        finish = "Draw!";
      }
      printCenter(finish);
      getch();
     // endwin();
      return;
    }
  } 
}

void View::gameCycle()
{
  std::string message;
  char ch = 0;
  keypad (stdscr, true);
  printCenter("Game starts right now!");
  while(1)
  {
    duelWinner_ = model_->getDuelWinner();
    showCurrentPos();
    
    ch = getch();

    std::string quitString = settings_.get("KEY_QUIT");
    std::stringstream quitStr;
    quitStr << ch;
    if (quitString == quitStr.str())
    //if (ch == 'q')
    {
      endwin();
      return;
    }



    //ask players for key
    int keyFound = 0;
    for (int i = 0; i < playersNumber_; ++i)
    {
      keyFound = players_[i]->checkKey(ch);
      if (keyFound)
      {
        break;
      }
    }

    //check the winner
    if (!model_->isGameStarted())
    {
      int winner = model_->getWhoIsWinner();
      std::string finish;

      if (winner != 0)
      {
        finish = model_->getPlayerName(winner);
        finish += " is winner! Bye!";
      }
      else
      {
        finish = "Draw!";
      }
      printCenter(finish);
      getch();
      endwin();
      return;
    }
  }
}


void View::printCenter(const std::string &message) const
{
  clear();
  mvaddstr(LINES / 2, COLS / 2, message.c_str());
  move(LINES / 2 + 1, COLS / 2);
  refresh();
  return;
}

void View::printTop(const std::string &message) const
{
  move(0,0);
  clrtoeol();
  addstr(message.c_str());
  refresh();
  return;
}

void View::printCard(int y, int x, int id, int color) const
{
  if ((id >= cardFileNames_.size()) || (id < 0))
  {
    return;
  }

  std::ifstream f;
  f.open(cardFileNames_[id].c_str());

  if (f == 0)
  {
    f.close();
    return;
  }
  //read card from it's filename and print it string by string
  attron(COLOR_PAIR((color + 1)));
 //??????????????????????????????????
  char string[256];
  while(!f.eof())
  {
    f.getline(string, 256);
    move(y++, x);
    addstr(string);
  }
//???????????????????????????????????
  attroff(COLOR_PAIR((color + 1)));
  f.close();
  return;
}
void View::prepareColors() const
{
  start_color();
  init_pair(1, COLOR_YELLOW, COLOR_BLACK);
  init_pair(2, COLOR_RED, COLOR_BLACK);
  init_pair(3, COLOR_GREEN, COLOR_BLACK);
  init_pair(4, COLOR_BLUE, COLOR_BLACK);
  init_pair(5, COLOR_WHITE, COLOR_BLACK);
}

void View::startSettings()
{
  char c = 0;
  std::stringstream s;
  s << "Network or local game? ";
  s << settings_.get("NETWORK_GAME_KEY");
  s << " / ";
  s << settings_.get("LOCAL_GAME_KEY");
  printCenter(s.str());
  //log("begin");
  bool flag = false;
  while (!flag)
  {
    move(LINES / 2 + 1, COLS / 2);
    clrtoeol();
    c = getch();
    s.str("");
    s << c;
    if(s.str() == settings_.get("NETWORK_GAME_KEY"))
    {
      //connector_ = new NetworkConnector();
      gameType = NETWORK;
      flag = true;
    }
    else if (s.str() == settings_.get("LOCAL_GAME_KEY"))
    {
      connector_ = new Connector();
      connector_->setModel(*model_);
      gameType = LOCAL;
      flag = true;
    }
  }
  
  if (gameType == LOCAL)
  {
    s.str("");
    s << "How many players on the computer? ";
    s << 2;
    s << " to ";
    s << MAX_PLAYER_SINGLE_MACHINE;
    printCenter(s.str());
    bool isNumber = false;
    while (!isNumber)
    {
      move(LINES / 2 + 1, COLS / 2);
      clrtoeol();
      c = getch();
      if ((c >= '2') && (c <= MAX_PLAYER_SINGLE_MACHINE + 48))
      {
        //log(c);
        isNumber = true;
      }    
    }
    playersNumber_ = c - 48; 

    for (int i = 0; i != playersNumber_; ++i)
    {
      players_[i] = new Player(*connector_);
      //addPlayer(new Player(*connector_));
    }
  }
  
  else if (gameType == NETWORK)
  {
    s.str("");
    s << "Server or client? ";
    s << settings_.get("SERVER_KEY");
    s << " / ";
    s << settings_.get("CLIENT_KEY");
    printCenter(s.str());
    bool flag = false;
    while (!flag)
    {
      move(LINES / 2 + 1, COLS / 2);
      clrtoeol();
      c = getch();
      s.str("");
      s << c;
      if(s.str() == settings_.get("SERVER_KEY"))
      {
        server = new ServerConnector();
        userType = SERVER;
        flag = true;
        server->setModel(*model_);
        //playersNumber_ = 1;
       // log("playesNumber_:", DEBUG);
        //log(playersNumber_, DEBUG);
        addPlayer(new Player(*server));
      }
      else if (s.str() == settings_.get("CLIENT_KEY"))
      {
        //connector_ = new NetworkConnector();
        client = new NetworkConnector();
        userType =  CLIENT;
        flag = true;
        client->setModel(*model_);
        addPlayer(new Player(*client));
      }
    }
  }
}

View::View():connector_(0), model_(0), playersNumber_(0), settings_(Settings::getInstance()), 
            gameType(LOCAL), userType(CLIENT)
{
  setCardFileNames();
}

View::View(Connector &connector):connector_(&connector),model_(connector.getModel()), playersNumber_(0),
                                   settings_(Settings::getInstance()),gameType(LOCAL), userType(CLIENT)
{
  players_.resize(GameModel::MAX_PLAYERS_NUM_);
  setCardFileNames();
}


View::View(GameModel &model):model_(&model), playersNumber_(0), settings_(Settings::getInstance()),
                             gameType(LOCAL), userType(CLIENT)
{
  players_.resize(GameModel::MAX_PLAYERS_NUM_);
  setCardFileNames();
}

View::~View()
{
  for (int i = 0; i != playersNumber_; ++i)
  {
    delete players_[i];
  }
  if (gameType == LOCAL)
  {
    delete connector_;
  }
  else 
  {
    if (userType == SERVER)
    {
      delete server;
    }
    else
    {
      delete client;
    }
  }
}

int View::addPlayer(Player *player)
{
  if (playersNumber_ >= GameModel::MAX_PLAYERS_NUM_)
  {
    return 1;
  }
  players_[playersNumber_] = player;
  ++playersNumber_;
  return 0;
}



void View::setCardFileNames()
{
  cardFileNames_.reserve(model_->USUAL_CARDS_NUM_ / model_->EVERY_CARD_TYPE_NUM_ + 1);
  std::string cardFileName;
  std::stringstream cardNumStr;
  std::string cardStr;
  for (int i = 0; i != model_->USUAL_CARDS_NUM_ / model_->EVERY_CARD_TYPE_NUM_; ++i)
  {
    // stream need to be cleared!!!!!
    std::stringstream cardNumStr;
    //!!!!!!!!!!!!!!!!!!!!!!!!!!11
    cardNumStr << i + 1;
    cardStr = "CARD" + cardNumStr.str() + "_FILENAME";
    cardFileName = settings_.get(cardStr);
    cardFileNames_.push_back(cardFileName);
    //cardFileNames_[i] = cardFileName;
  }

  cardStr = "CARD_ARROWS_INSIDE_FILENAME";
  cardFileName = settings_.get(cardStr);
  cardFileNames_.push_back(cardFileName);
  //cardFileNames_[model_->USUAL_CARDS_NUM_ / model_->EVERY_CARD_TYPE_NUM_] = cardFileName;
}


int View::setConnector(Connector &connector)
{
 if(connector_ != 0)
  {
    return 1;
  }
  connector_ = &connector;
  model_ = connector.getModel();
  players_.resize(GameModel::MAX_PLAYERS_NUM_);
  return 0;
}

//start game
void View::start()
{

  initscr();
  prepareColors();
  startSettings();
  connectPlayers();
 
  //curs_set(0);
  sleep(1);
  if (gameType == LOCAL)
  {
    gameCycle();
  }
  else if (gameType == NETWORK)
  {
    networkGameCycle();
  }
  endwin();
  return;
}






